iT邦幫忙

2025 iThome 鐵人賽

DAY 13
0
Modern Web

Modern Web:從基礎、框架到前端學習系列 第 25

Day 25 - To-Do List 進階版:完成狀態 + 暗黑模式(含 LocalStorage 持久化)

  • 分享至 

  • xImage
  •  

今天我們要升級前一堂課的作品!
你將學會如何讓使用者可以:

  1. 勾選任務完成狀態(✔️)
  2. 切換「亮色 / 暗黑模式」🌙
  3. 保存這些設定在 localStorage,重新整理也不會消失。

今日目標

  • 讓代辦事項能被標記為「完成」
  • 支援「刪除」與「新增」
  • 加入「暗黑模式切換」
  • 讓所有狀態在重新整理後依然保存

主要概念

1.任務資料結構

tasks = [
  { text: "學習 JavaScript", done: false },
  { text: "練習 LocalStorage", done: true }
];

2.儲存使用者設定

localStorage.setItem("theme", "dark"); // 保存主題模式

實作範例程式

<!DOCTYPE html>
<html lang="zh-Hant">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Day25 - To-Do List 進階版</title>
  <style>
    :root {
      --bg-color: #f5f7fa;
      --text-color: #333;
      --card-color: white;
      --btn-color: #4caf50;
      --btn-text: white;
    }

    body.dark {
      --bg-color: #1e1e1e;
      --text-color: #f5f5f5;
      --card-color: #2c2c2c;
      --btn-color: #2196f3;
      --btn-text: #fff;
    }

    body {
      background-color: var(--bg-color);
      color: var(--text-color);
      font-family: "Poppins", sans-serif;
      display: flex;
      flex-direction: column;
      align-items: center;
      padding: 40px;
      transition: 0.3s ease-in-out;
    }

    h2 {
      margin-bottom: 20px;
    }

    .controls {
      display: flex;
      justify-content: center;
      align-items: center;
      margin-bottom: 15px;
    }

    input {
      padding: 10px;
      width: 250px;
      border: 1px solid #ccc;
      border-radius: 8px;
    }

    button {
      padding: 10px 15px;
      margin-left: 10px;
      background: var(--btn-color);
      color: var(--btn-text);
      border: none;
      border-radius: 8px;
      cursor: pointer;
      transition: background 0.3s;
    }

    button:hover {
      opacity: 0.9;
    }

    ul {
      list-style: none;
      padding: 0;
      width: 300px;
      margin-top: 20px;
    }

    li {
      background: var(--card-color);
      border: 1px solid #ddd;
      border-radius: 8px;
      padding: 10px;
      margin-bottom: 8px;
      display: flex;
      justify-content: space-between;
      align-items: center;
    }

    .done {
      text-decoration: line-through;
      opacity: 0.6;
    }

    .actions {
      display: flex;
      gap: 10px;
    }

    .delete {
      color: red;
      cursor: pointer;
    }

    .toggle-theme {
      margin-top: 20px;
      background: none;
      border: 2px solid var(--btn-color);
      color: var(--btn-color);
      padding: 8px 15px;
      border-radius: 8px;
      cursor: pointer;
      transition: 0.3s;
    }

    .toggle-theme:hover {
      background: var(--btn-color);
      color: white;
    }

    @media (max-width: 500px) {
      ul {
        width: 90%;
      }
    }
  </style>
</head>
<body>
  <h2>📝 To-Do List 進階版</h2>
  <div class="controls">
    <input type="text" id="taskInput" placeholder="輸入代辦事項...">
    <button id="addBtn">新增</button>
  </div>
  <ul id="taskList"></ul>
  <button class="toggle-theme" id="themeBtn">切換主題</button>

  <script>
    const taskInput = document.getElementById("taskInput");
    const addBtn = document.getElementById("addBtn");
    const taskList = document.getElementById("taskList");
    const themeBtn = document.getElementById("themeBtn");

    // 初始化:從 localStorage 讀取資料
    let tasks = JSON.parse(localStorage.getItem("tasks")) || [];
    let theme = localStorage.getItem("theme") || "light";

    // 套用主題
    document.body.classList.toggle("dark", theme === "dark");

    // 顯示任務列表
    function renderTasks() {
      taskList.innerHTML = "";
      tasks.forEach((task, index) => {
        const li = document.createElement("li");
        li.innerHTML = `
          <span class="${task.done ? 'done' : ''}" onclick="toggleDone(${index})">${task.text}</span>
          <div class="actions">
            <span class="delete" onclick="deleteTask(${index})">✖</span>
          </div>
        `;
        taskList.appendChild(li);
      });
    }

    // 新增任務
    addBtn.addEventListener("click", () => {
      const text = taskInput.value.trim();
      if (text) {
        tasks.push({ text, done: false });
        updateStorage();
        renderTasks();
        taskInput.value = "";
      }
    });

    // 切換完成狀態
    function toggleDone(index) {
      tasks[index].done = !tasks[index].done;
      updateStorage();
      renderTasks();
    }

    // 刪除任務
    function deleteTask(index) {
      tasks.splice(index, 1);
      updateStorage();
      renderTasks();
    }

    // 更新 localStorage
    function updateStorage() {
      localStorage.setItem("tasks", JSON.stringify(tasks));
    }

    // 主題切換
    themeBtn.addEventListener("click", () => {
      document.body.classList.toggle("dark");
      theme = document.body.classList.contains("dark") ? "dark" : "light";
      localStorage.setItem("theme", theme);
    });

    // 載入時渲染
    renderTasks();
  </script>
</body>
</html>

今日小挑戰

  1. 為每個任務加入「建立時間」並顯示在項目下方。
  2. 新增「全部清除」按鈕,能刪除所有任務。
  3. 將清單資料導出成 JSON 檔案(進階應用)。

上一篇
Day 24 - LocalStorage 進階應用與資料持久化實作
系列文
Modern Web:從基礎、框架到前端學習25
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言